import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;

import org.jfree.data.general.DefaultPieDataset;

public class Stats
{	
	// stat[décalage_anticodon, position, seq[position]] = x fois
	private HashMap<String, StatsTri> statslist = new HashMap<String, StatsTri>();
	
	private String[] X  = {   "AAC", "AAT", "ACC", "ATC", "ATT", "CAG", "CTC", "CTG", "GAA", "GAC",
	"GAG", "GAT", "GCC", "GGC", "GGT", "GTA", "GTC", "GTT", "TAC", "TTC" };
	private String[] X1 = {   "AAG", "ACA", "ACG", "ACT", "AGC", "AGG", "ATA", "ATG", "CCA", "CCG",
	"GCG", "GTG", "TAG", "TCA", "TCC", "TCG", "TCT", "TGC", "TTA", "TTG" };
	private String[] X2 = {   "AGA", "AGT", "CAA", "CAC", "CAT", "CCT", "CGA", "CGC", "CGG", "CGT",
	"CTA", "CTT", "GCA", "GCT", "GGA", "TAA", "TAT", "TGA", "TGG", "TGT" };
	private String[] Xid = { "AAA", "CCC", "GGG", "TTT" };
	
	// Constructors
	
	public Stats () {}
	
	public void cut(String seq, String ac, int pos)
	{
		// Pour chaque trinucléotide, à partir de l'anticodon, on l'ajoute aux stats
		String tri = "";
		int    len = seq.length();
		int    off = 0;
		//System.out.println(ac + "(" + pos + ") =>  " + seq.substring(pos, pos+3));
		
		// Pour les trois décalages possibles (0, 0+1, 0+2)
		for (int dec=0; dec<3; dec++)
		{
			// On parcourt la chaîne pour traiter les différentes positions
			for (int i=(pos+dec+1)%3; i<len; i+=3)
			{
				// Si le trinucléotide courant est complet
				if (i+3 <= len)
				{					
					tri = seq.substring(i, i+3);
					off = (i-pos)/3;
					
					// On l'ajoute aux stats
					add(tri, dec, off);
					
					// Codage en X
					if (Arrays.asList(X).contains(tri))
					{
						add("X", dec+3, off);
					}
					else if (Arrays.asList(X1).contains(tri))
					{
						add("X1", dec+3, off);
					}
					else if (Arrays.asList(X2).contains(tri))
					{
						add("X2", dec+3, off);
					}
					else if (Arrays.asList(Xid).contains(tri))
					{
						add("Xid", dec+3, off);
					}
				}
			}
		}
	}
	
	private void add(String tri, int dec, int off)
	{
		StatsTri st = null;
		String   i  = dec  + ":" + off;
		
		// On regarde si on a déjà trouvé un trinucléotide à cet endroit
		if ((st = (StatsTri) statslist.get(i)) != null)
		{ 
			// Si c'est le cas, on note qu'il est apparu +1 fois ici
			//System.out.println("+1");
			st.seen(tri);
		}
		else
		{
			// Sinon on l'ajoute à la liste
			//System.out.println("!");
			st = new StatsTri(dec, off, tri);
			statslist.put(i, st);
		}
	}
	
	public void save(String file) throws IOException
	{	
		// On récupère le résultat des statistiques
		ArrayList<StatsTri> slist = new ArrayList<StatsTri>(statslist.values());
		// Que l'on trie
		Collections.sort(slist);
		
		// On enregistre le contenu des stats dans une liste de chaînes
		// qui est de taille <apparition maximale d'un trinucléotide différent
		// pour n'importe quelle position>, initialisé à "" partout
		// On utilise 2 lignes par trinucléotide + 1 ligne de header
		ArrayList<String> line = new ArrayList<String>();
		for (int i=0; i<StatsTri.max*3+1; i++)
		{
			line.add("");
		}
		
		// Pour chaque position possible	
		for (StatsTri st : slist)
		{
			// On ajoute en header la position courante
			String h = line.get(0);
			if (st.off == 0)
			{
				// Codage trinucléotides
				if (st.dec < 3)
				{
					h += "   0";
					if (st.dec > 0)
					{
						h += "+" + st.dec;
					}
					h += ";";
				}
				else // Codage en X
				{
					h += "   X";
					if (st.dec % 3 > 0)
					{
						int d = st.dec%3;
						h += "+" + d;
					}
					h += ";";
				}
				
			}
			else
			{
				h += "   " + st.off +";";
			}
			line.set(0,h);
		
			//System.out.println("[" + st.dec + "/" + st.off + "] : " + st.cpt);
			
			// On trie les trinucléotides selon leur apparition
			ArrayList<Map.Entry<String, Integer>> l =
					new ArrayList<Entry<String, Integer>>(st.trilist.entrySet());
			Collections.sort(l, new CompareTri());
		
			// On parcourt les trinucléotides de cette position;
			for (int i = 1; i<= StatsTri.max; i++)
			{
				try
				{
					// On récupère le trinucléotide et le nombre de fois qu'il est apparu
					Map.Entry<String, Integer> tri = l.get(i-1);
					String stri = tri.getKey();
					int    val  = tri.getValue();
					float  p    = (float) (Math.floor(((float)val)/st.cpt*10000)/100);
					
					// On l'ajoute au fichier
					int j = (i-1)*3+1;
					line.set(j,   line.get(j) + stri + " ;");
					line.set(j+1, line.get(j+1) + val + " ;");
					line.set(j+2, line.get(j+2) + p + "%;");
				}
				catch (IndexOutOfBoundsException e)
				{
					// Si on a plus de trinucléotide pour cette position
					// on met une case vide dans le fichier
					int j = (i-1)*3+1;
					line.set(j, line.get(j) + ";");
					line.set(j+1, line.get(j+1) + ";");
					line.set(j+2, line.get(j+2) + ";");
				}
			}
			
			// On écrit notre buffer dans le fichier !
			BufferedWriter stream = new BufferedWriter(new FileWriter(file));						
			for (String cl : line)
			{
				//System.out.println(cl);
				stream.write(cl + "\n");
			}	
			stream.close();
		}
	}
	
	public void graphbest(DefaultPieDataset data)
	{
		// On récupère les 5 meilleurs trinucléotides pour l'anticodon décalé de 0
		StatsTri st = this.statslist.get("0:0");
		
		// On trie les trinucléotides selon leur apparition
		ArrayList<Map.Entry<String, Integer>> l =
				new ArrayList<Entry<String, Integer>>(st.trilist.entrySet());
		Collections.sort(l, new CompareTri());
		
		// On prend les premières qu'on ajoute au graphe!
		for (int i=0; i<7; i++)
		{
			try
			{
				Map.Entry<String, Integer> tri = l.get(i-1);
				String stri = tri.getKey();
				int    val  = tri.getValue();
				float  p    = (float) (Math.floor(((float)val)/st.cpt*10000)/100);
				
				data.setValue(stri + " " + p + "%", p);
			}
			catch (IndexOutOfBoundsException e){}
		}
	}
	
	// Méthode de tri des différents trinucléotides pour chaque position
	private class CompareTri implements Comparator<Map.Entry<String, Integer>>
	{
		public int compare(Map.Entry<String, Integer> t1, Map.Entry<String, Integer> t2) {
			return t2.getValue().compareTo(t1.getValue());
		}   
    } 
}
